/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.

This file is part of Quake III Arena source code.

Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

/*****************************************************************************
 * name:		be_ai_weight.c
 *
 * desc:		fuzzy logic
 *
 * $Archive: /code/botlib/be_ai_weight.c $
 *
 *****************************************************************************/

#include "../game/q_shared.h"
#include "l_memory.h"
#include "l_log.h"
#include "l_utils.h"
#include "l_script.h"
#include "l_precomp.h"
#include "l_struct.h"
#include "l_libvar.h"
#include "aasfile.h"
#include "../game/botlib.h"
#include "../game/be_aas.h"
#include "be_aas_funcs.h"
#include "be_interface.h"
#include "be_ai_weight.h"

#define MAX_INVENTORYVALUE			999999
#define EVALUATERECURSIVELY

#define MAX_WEIGHT_FILES			128
weightconfig_t	*weightFileList[MAX_WEIGHT_FILES];

//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int ReadValue(source_t *source, float *value)
{
    token_t token;

    if (!PC_ExpectAnyToken(source, &token)) return qfalse;
    if (!strcmp(token.string, "-"))
    {
        SourceWarning(source, "negative value set to zero\n");
        if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) return qfalse;
    } //end if
    if (token.type != TT_NUMBER)
    {
        SourceError(source, "invalid return value %s\n", token.string);
        return qfalse;
    } //end if
    *value = token.floatvalue;
    return qtrue;
} //end of the function ReadValue
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int ReadFuzzyWeight(source_t *source, fuzzyseperator_t *fs)
{
    if (PC_CheckTokenString(source, "balance"))
    {
        fs->type = WT_BALANCE;
        if (!PC_ExpectTokenString(source, "(")) return qfalse;
        if (!ReadValue(source, &fs->weight)) return qfalse;
        if (!PC_ExpectTokenString(source, ",")) return qfalse;
        if (!ReadValue(source, &fs->minweight)) return qfalse;
        if (!PC_ExpectTokenString(source, ",")) return qfalse;
        if (!ReadValue(source, &fs->maxweight)) return qfalse;
        if (!PC_ExpectTokenString(source, ")")) return qfalse;
    } //end if
    else
    {
        fs->type = 0;
        if (!ReadValue(source, &fs->weight)) return qfalse;
        fs->minweight = fs->weight;
        fs->maxweight = fs->weight;
    } //end if
    if (!PC_ExpectTokenString(source, ";")) return qfalse;
    return qtrue;
} //end of the function ReadFuzzyWeight
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void FreeFuzzySeperators_r(fuzzyseperator_t *fs)
{
    if (!fs) return;
    if (fs->child) FreeFuzzySeperators_r(fs->child);
    if (fs->next) FreeFuzzySeperators_r(fs->next);
    FreeMemory(fs);
} //end of the function FreeFuzzySeperators
//===========================================================================
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//===========================================================================
void FreeWeightConfig2(weightconfig_t *config)
{
    int i;

    for (i = 0; i < config->numweights; i++)
    {
        FreeFuzzySeperators_r(config->weights[i].firstseperator);
        if (config->weights[i].name) FreeMemory(config->weights[i].name);
    } //end for
    FreeMemory(config);
} //end of the function FreeWeightConfig2
//===========================================================================
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//===========================================================================
void FreeWeightConfig(weightconfig_t *config)
{
    if (!LibVarGetValue("bot_reloadcharacters")) return;
    FreeWeightConfig2(config);
} //end of the function FreeWeightConfig
//===========================================================================
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//===========================================================================
fuzzyseperator_t *ReadFuzzySeperators_r(source_t *source)
{
    int newindent, index, def, founddefault;
    token_t token;
    fuzzyseperator_t *fs, *lastfs, *firstfs;

    founddefault = qfalse;
    firstfs = NULL;
    lastfs = NULL;
    if (!PC_ExpectTokenString(source, "(")) return NULL;
    if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) return NULL;
    index = token.intvalue;
    if (!PC_ExpectTokenString(source, ")")) return NULL;
    if (!PC_ExpectTokenString(source, "{")) return NULL;
    if (!PC_ExpectAnyToken(source, &token)) return NULL;
    do
    {
        def = !strcmp(token.string, "default");
        if (def || !strcmp(token.string, "case"))
        {
            fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));
            fs->index = index;
            if (lastfs) lastfs->next = fs;
            else firstfs = fs;
            lastfs = fs;
            if (def)
            {
                if (founddefault)
                {
                    SourceError(source, "switch already has a default\n");
                    FreeFuzzySeperators_r(firstfs);
                    return NULL;
                } //end if
                fs->value = MAX_INVENTORYVALUE;
                founddefault = qtrue;
            } //end if
            else
            {
                if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token))
                {
                    FreeFuzzySeperators_r(firstfs);
                    return NULL;
                } //end if
                fs->value = token.intvalue;
            } //end else
            if (!PC_ExpectTokenString(source, ":") || !PC_ExpectAnyToken(source, &token))
            {
                FreeFuzzySeperators_r(firstfs);
                return NULL;
            } //end if
            newindent = qfalse;
            if (!strcmp(token.string, "{"))
            {
                newindent = qtrue;
                if (!PC_ExpectAnyToken(source, &token))
                {
                    FreeFuzzySeperators_r(firstfs);
                    return NULL;
                } //end if
            } //end if
            if (!strcmp(token.string, "return"))
            {
                if (!ReadFuzzyWeight(source, fs))
                {
                    FreeFuzzySeperators_r(firstfs);
                    return NULL;
                } //end if
            } //end if
            else if (!strcmp(token.string, "switch"))
            {
                fs->child = ReadFuzzySeperators_r(source);
                if (!fs->child)
                {
                    FreeFuzzySeperators_r(firstfs);
                    return NULL;
                } //end if
            } //end else if
            else
            {
                SourceError(source, "invalid name %s\n", token.string);
                return NULL;
            } //end else
            if (newindent)
            {
                if (!PC_ExpectTokenString(source, "}"))
                {
                    FreeFuzzySeperators_r(firstfs);
                    return NULL;
                } //end if
            } //end if
        } //end if
        else
        {
            FreeFuzzySeperators_r(firstfs);
            SourceError(source, "invalid name %s\n", token.string);
            return NULL;
        } //end else
        if (!PC_ExpectAnyToken(source, &token))
        {
            FreeFuzzySeperators_r(firstfs);
            return NULL;
        } //end if
    } while(strcmp(token.string, "}"));
    //
    if (!founddefault)
    {
        SourceWarning(source, "switch without default\n");
        fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));
        fs->index = index;
        fs->value = MAX_INVENTORYVALUE;
        fs->weight = 0;
        fs->next = NULL;
        fs->child = NULL;
        if (lastfs) lastfs->next = fs;
        else firstfs = fs;
        lastfs = fs;
    } //end if
    //
    return firstfs;
} //end of the function ReadFuzzySeperators_r
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
weightconfig_t *ReadWeightConfig(char *filename)
{
    int newindent, avail = 0, n;
    token_t token;
    source_t *source;
    fuzzyseperator_t *fs;
    weightconfig_t *config = NULL;
#ifdef DEBUG
    int starttime;

    starttime = Sys_MilliSeconds();
#endif //DEBUG

    if (!LibVarGetValue("bot_reloadcharacters"))
    {
        avail = -1;
        for( n = 0; n < MAX_WEIGHT_FILES; n++ )
        {
            config = weightFileList[n];
            if( !config )
            {
                if( avail == -1 )
                {
                    avail = n;
                } //end if
                continue;
            } //end if
            if( strcmp( filename, config->filename ) == 0 )
            {
                //botimport.Print( PRT_MESSAGE, "retained %s\n", filename );
                return config;
            } //end if
        } //end for

        if( avail == -1 )
        {
            botimport.Print( PRT_ERROR, "weightFileList was full trying to load %s\n", filename );
            return NULL;
        } //end if
    } //end if

    PC_SetBaseFolder(BOTFILESBASEFOLDER);
    source = LoadSourceFile(filename);
    if (!source)
    {
        botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
        return NULL;
    } //end if
    //
    config = (weightconfig_t *) GetClearedMemory(sizeof(weightconfig_t));
    config->numweights = 0;
    Q_strncpyz( config->filename, filename, sizeof(config->filename) );
    //parse the item config file
    while(PC_ReadToken(source, &token))
    {
        if (!strcmp(token.string, "weight"))
        {
            if (config->numweights >= MAX_WEIGHTS)
            {
                SourceWarning(source, "too many fuzzy weights\n");
                break;
            } //end if
            if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
            {
                FreeWeightConfig(config);
                FreeSource(source);
                return NULL;
            } //end if
            StripDoubleQuotes(token.string);
            config->weights[config->numweights].name = (char *) GetClearedMemory(strlen(token.string) + 1);
            strcpy(config->weights[config->numweights].name, token.string);
            if (!PC_ExpectAnyToken(source, &token))
            {
                FreeWeightConfig(config);
                FreeSource(source);
                return NULL;
            } //end if
            newindent = qfalse;
            if (!strcmp(token.string, "{"))
            {
                newindent = qtrue;
                if (!PC_ExpectAnyToken(source, &token))
                {
                    FreeWeightConfig(config);
                    FreeSource(source);
                    return NULL;
                } //end if
            } //end if
            if (!strcmp(token.string, "switch"))
            {
                fs = ReadFuzzySeperators_r(source);
                if (!fs)
                {
                    FreeWeightConfig(config);
                    FreeSource(source);
                    return NULL;
                } //end if
                config->weights[config->numweights].firstseperator = fs;
            } //end if
            else if (!strcmp(token.string, "return"))
            {
                fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));
                fs->index = 0;
                fs->value = MAX_INVENTORYVALUE;
                fs->next = NULL;
                fs->child = NULL;
                if (!ReadFuzzyWeight(source, fs))
                {
                    FreeMemory(fs);
                    FreeWeightConfig(config);
                    FreeSource(source);
                    return NULL;
                } //end if
                config->weights[config->numweights].firstseperator = fs;
            } //end else if
            else
            {
                SourceError(source, "invalid name %s\n", token.string);
                FreeWeightConfig(config);
                FreeSource(source);
                return NULL;
            } //end else
            if (newindent)
            {
                if (!PC_ExpectTokenString(source, "}"))
                {
                    FreeWeightConfig(config);
                    FreeSource(source);
                    return NULL;
                } //end if
            } //end if
            config->numweights++;
        } //end if
        else
        {
            SourceError(source, "invalid name %s\n", token.string);
            FreeWeightConfig(config);
            FreeSource(source);
            return NULL;
        } //end else
    } //end while
    //free the source at the end of a pass
    FreeSource(source);
    //if the file was located in a pak file
    botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
#ifdef DEBUG
    if (bot_developer->integer)
    {
        botimport.Print(PRT_MESSAGE, "weights loaded in %d msec\n", Sys_MilliSeconds() - starttime);
    } //end if
#endif //DEBUG
    //
    if (!LibVarGetValue("bot_reloadcharacters"))
    {
        weightFileList[avail] = config;
    } //end if
    //
    return config;
} //end of the function ReadWeightConfig
#if 0
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
qboolean WriteFuzzyWeight(FILE *fp, fuzzyseperator_t *fs)
{
    if (fs->type == WT_BALANCE)
    {
        if (fprintf(fp, " return balance(") < 0) return qfalse;
        if (!WriteFloat(fp, fs->weight)) return qfalse;
        if (fprintf(fp, ",") < 0) return qfalse;
        if (!WriteFloat(fp, fs->minweight)) return qfalse;
        if (fprintf(fp, ",") < 0) return qfalse;
        if (!WriteFloat(fp, fs->maxweight)) return qfalse;
        if (fprintf(fp, ");\n") < 0) return qfalse;
    } //end if
    else
    {
        if (fprintf(fp, " return ") < 0) return qfalse;
        if (!WriteFloat(fp, fs->weight)) return qfalse;
        if (fprintf(fp, ";\n") < 0) return qfalse;
    } //end else
    return qtrue;
} //end of the function WriteFuzzyWeight
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
qboolean WriteFuzzySeperators_r(FILE *fp, fuzzyseperator_t *fs, int indent)
{
    if (!WriteIndent(fp, indent)) return qfalse;
    if (fprintf(fp, "switch(%d)\n", fs->index) < 0) return qfalse;
    if (!WriteIndent(fp, indent)) return qfalse;
    if (fprintf(fp, "{\n") < 0) return qfalse;
    indent++;
    do
    {
        if (!WriteIndent(fp, indent)) return qfalse;
        if (fs->next)
        {
            if (fprintf(fp, "case %d:", fs->value) < 0) return qfalse;
        } //end if
        else
        {
            if (fprintf(fp, "default:") < 0) return qfalse;
        } //end else
        if (fs->child)
        {
            if (fprintf(fp, "\n") < 0) return qfalse;
            if (!WriteIndent(fp, indent)) return qfalse;
            if (fprintf(fp, "{\n") < 0) return qfalse;
            if (!WriteFuzzySeperators_r(fp, fs->child, indent + 1)) return qfalse;
            if (!WriteIndent(fp, indent)) return qfalse;
            if (fs->next)
            {
                if (fprintf(fp, "} //end case\n") < 0) return qfalse;
            } //end if
            else
            {
                if (fprintf(fp, "} //end default\n") < 0) return qfalse;
            } //end else
        } //end if
        else
        {
            if (!WriteFuzzyWeight(fp, fs)) return qfalse;
        } //end else
        fs = fs->next;
    } while(fs);
    indent--;
    if (!WriteIndent(fp, indent)) return qfalse;
    if (fprintf(fp, "} //end switch\n") < 0) return qfalse;
    return qtrue;
} //end of the function WriteItemFuzzyWeights_r
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
qboolean WriteWeightConfig(char *filename, weightconfig_t *config)
{
    int i;
    FILE *fp;
    weight_t *ifw;

    fp = fopen(filename, "wb");
    if (!fp) return qfalse;

    for (i = 0; i < config->numweights; i++)
    {
        ifw = &config->weights[i];
        if (fprintf(fp, "\nweight \"%s\"\n", ifw->name) < 0) return qfalse;
        if (fprintf(fp, "{\n") < 0) return qfalse;
        if (ifw->firstseperator->index > 0)
        {
            if (!WriteFuzzySeperators_r(fp, ifw->firstseperator, 1)) return qfalse;
        } //end if
        else
        {
            if (!WriteIndent(fp, 1)) return qfalse;
            if (!WriteFuzzyWeight(fp, ifw->firstseperator)) return qfalse;
        } //end else
        if (fprintf(fp, "} //end weight\n") < 0) return qfalse;
    } //end for
    fclose(fp);
    return qtrue;
} //end of the function WriteWeightConfig
#endif
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int FindFuzzyWeight(weightconfig_t *wc, char *name)
{
    int i;

    for (i = 0; i < wc->numweights; i++)
    {
        if (!strcmp(wc->weights[i].name, name))
        {
            return i;
        } //end if
    } //end if
    return -1;
} //end of the function FindFuzzyWeight
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
float FuzzyWeight_r(int *inventory, fuzzyseperator_t *fs)
{
    float scale, w1, w2;

    if (inventory[fs->index] < fs->value)
    {
        if (fs->child) return FuzzyWeight_r(inventory, fs->child);
        else return fs->weight;
    } //end if
    else if (fs->next)
    {
        if (inventory[fs->index] < fs->next->value)
        {
            //first weight
            if (fs->child) w1 = FuzzyWeight_r(inventory, fs->child);
            else w1 = fs->weight;
            //second weight
            if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child);
            else w2 = fs->next->weight;
            //the scale factor
            scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value);
            //scale between the two weights
            return scale * w1 + (1 - scale) * w2;
        } //end if
        return FuzzyWeight_r(inventory, fs->next);
    } //end else if
    return fs->weight;
} //end of the function FuzzyWeight_r
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
float FuzzyWeightUndecided_r(int *inventory, fuzzyseperator_t *fs)
{
    float scale, w1, w2;

    if (inventory[fs->index] < fs->value)
    {
        if (fs->child) return FuzzyWeightUndecided_r(inventory, fs->child);
        else return fs->minweight + random() * (fs->maxweight - fs->minweight);
    } //end if
    else if (fs->next)
    {
        if (inventory[fs->index] < fs->next->value)
        {
            //first weight
            if (fs->child) w1 = FuzzyWeightUndecided_r(inventory, fs->child);
            else w1 = fs->minweight + random() * (fs->maxweight - fs->minweight);
            //second weight
            if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child);
            else w2 = fs->next->minweight + random() * (fs->next->maxweight - fs->next->minweight);
            //the scale factor
            scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value);
            //scale between the two weights
            return scale * w1 + (1 - scale) * w2;
        } //end if
        return FuzzyWeightUndecided_r(inventory, fs->next);
    } //end else if
    return fs->weight;
} //end of the function FuzzyWeightUndecided_r
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum)
{
#ifdef EVALUATERECURSIVELY
    return FuzzyWeight_r(inventory, wc->weights[weightnum].firstseperator);
#else
    fuzzyseperator_t *s;

    s = wc->weights[weightnum].firstseperator;
    if (!s) return 0;
    while(1)
    {
        if (inventory[s->index] < s->value)
        {
            if (s->child) s = s->child;
            else return s->weight;
        } //end if
        else
        {
            if (s->next) s = s->next;
            else return s->weight;
        } //end else
    } //end if
    return 0;
#endif
} //end of the function FuzzyWeight
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum)
{
#ifdef EVALUATERECURSIVELY
    return FuzzyWeightUndecided_r(inventory, wc->weights[weightnum].firstseperator);
#else
    fuzzyseperator_t *s;

    s = wc->weights[weightnum].firstseperator;
    if (!s) return 0;
    while(1)
    {
        if (inventory[s->index] < s->value)
        {
            if (s->child) s = s->child;
            else return s->minweight + random() * (s->maxweight - s->minweight);
        } //end if
        else
        {
            if (s->next) s = s->next;
            else return s->minweight + random() * (s->maxweight - s->minweight);
        } //end else
    } //end if
    return 0;
#endif
} //end of the function FuzzyWeightUndecided
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void EvolveFuzzySeperator_r(fuzzyseperator_t *fs)
{
    if (fs->child)
    {
        EvolveFuzzySeperator_r(fs->child);
    } //end if
    else if (fs->type == WT_BALANCE)
    {
        //every once in a while an evolution leap occurs, mutation
        if (random() < 0.01) fs->weight += crandom() * (fs->maxweight - fs->minweight);
        else fs->weight += crandom() * (fs->maxweight - fs->minweight) * 0.5;
        //modify bounds if necesary because of mutation
        if (fs->weight < fs->minweight) fs->minweight = fs->weight;
        else if (fs->weight > fs->maxweight) fs->maxweight = fs->weight;
    } //end else if
    if (fs->next) EvolveFuzzySeperator_r(fs->next);
} //end of the function EvolveFuzzySeperator_r
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void EvolveWeightConfig(weightconfig_t *config)
{
    int i;

    for (i = 0; i < config->numweights; i++)
    {
        EvolveFuzzySeperator_r(config->weights[i].firstseperator);
    } //end for
} //end of the function EvolveWeightConfig
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void ScaleFuzzySeperator_r(fuzzyseperator_t *fs, float scale)
{
    if (fs->child)
    {
        ScaleFuzzySeperator_r(fs->child, scale);
    } //end if
    else if (fs->type == WT_BALANCE)
    {
        //
        fs->weight = (fs->maxweight + fs->minweight) * scale;
        //get the weight between bounds
        if (fs->weight < fs->minweight) fs->weight = fs->minweight;
        else if (fs->weight > fs->maxweight) fs->weight = fs->maxweight;
    } //end else if
    if (fs->next) ScaleFuzzySeperator_r(fs->next, scale);
} //end of the function ScaleFuzzySeperator_r
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void ScaleWeight(weightconfig_t *config, char *name, float scale)
{
    int i;

    if (scale < 0) scale = 0;
    else if (scale > 1) scale = 1;
    for (i = 0; i < config->numweights; i++)
    {
        if (!strcmp(name, config->weights[i].name))
        {
            ScaleFuzzySeperator_r(config->weights[i].firstseperator, scale);
            break;
        } //end if
    } //end for
} //end of the function ScaleWeight
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void ScaleFuzzySeperatorBalanceRange_r(fuzzyseperator_t *fs, float scale)
{
    if (fs->child)
    {
        ScaleFuzzySeperatorBalanceRange_r(fs->child, scale);
    } //end if
    else if (fs->type == WT_BALANCE)
    {
        float mid = (fs->minweight + fs->maxweight) * 0.5;
        //get the weight between bounds
        fs->maxweight = mid + (fs->maxweight - mid) * scale;
        fs->minweight = mid + (fs->minweight - mid) * scale;
        if (fs->maxweight < fs->minweight)
        {
            fs->maxweight = fs->minweight;
        } //end if
    } //end else if
    if (fs->next) ScaleFuzzySeperatorBalanceRange_r(fs->next, scale);
} //end of the function ScaleFuzzySeperatorBalanceRange_r
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void ScaleFuzzyBalanceRange(weightconfig_t *config, float scale)
{
    int i;

    if (scale < 0) scale = 0;
    else if (scale > 100) scale = 100;
    for (i = 0; i < config->numweights; i++)
    {
        ScaleFuzzySeperatorBalanceRange_r(config->weights[i].firstseperator, scale);
    } //end for
} //end of the function ScaleFuzzyBalanceRange
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int InterbreedFuzzySeperator_r(fuzzyseperator_t *fs1, fuzzyseperator_t *fs2,
                               fuzzyseperator_t *fsout)
{
    if (fs1->child)
    {
        if (!fs2->child || !fsout->child)
        {
            botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal child\n");
            return qfalse;
        } //end if
        if (!InterbreedFuzzySeperator_r(fs2->child, fs2->child, fsout->child))
        {
            return qfalse;
        } //end if
    } //end if
    else if (fs1->type == WT_BALANCE)
    {
        if (fs2->type != WT_BALANCE || fsout->type != WT_BALANCE)
        {
            botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal balance\n");
            return qfalse;
        } //end if
        fsout->weight = (fs1->weight + fs2->weight) / 2;
        if (fsout->weight > fsout->maxweight) fsout->maxweight = fsout->weight;
        if (fsout->weight > fsout->minweight) fsout->minweight = fsout->weight;
    } //end else if
    if (fs1->next)
    {
        if (!fs2->next || !fsout->next)
        {
            botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal next\n");
            return qfalse;
        } //end if
        if (!InterbreedFuzzySeperator_r(fs1->next, fs2->next, fsout->next))
        {
            return qfalse;
        } //end if
    } //end if
    return qtrue;
} //end of the function InterbreedFuzzySeperator_r
//===========================================================================
// config1 and config2 are interbreeded and stored in configout
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2,
                             weightconfig_t *configout)
{
    int i;

    if (config1->numweights != config2->numweights ||
            config1->numweights != configout->numweights)
    {
        botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal numweights\n");
        return;
    } //end if
    for (i = 0; i < config1->numweights; i++)
    {
        InterbreedFuzzySeperator_r(config1->weights[i].firstseperator,
                                   config2->weights[i].firstseperator,
                                   configout->weights[i].firstseperator);
    } //end for
} //end of the function InterbreedWeightConfigs
//===========================================================================
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//===========================================================================
void BotShutdownWeights(void)
{
    int i;

    for( i = 0; i < MAX_WEIGHT_FILES; i++ )
    {
        if (weightFileList[i])
        {
            FreeWeightConfig2(weightFileList[i]);
            weightFileList[i] = NULL;
        } //end if
    } //end for
} //end of the function BotShutdownWeights
